﻿
//
//  Copyright (c) 2011 Extreme: Paweł Huryn (lead), Jakub Domański
//

using System;
using System.Collections.Generic;
using System.Data.Linq;
using System.Linq;
using System.Linq.Expressions;

namespace Extreme.SPFramework.DomainModel
{
    /// <summary>
    /// LINQ Repository Base
    /// </summary>
    /// <typeparam name="TEntity">Entity type</typeparam>
    /// <typeparam name="TContext">Data context class</typeparam>
    public abstract class LinqRepositoryBase<TEntity, TContext> : IRepository<TEntity>
        where TEntity: LinqEntityBase, new() 
        where TContext: System.Data.Linq.DataContext, new()
    {
        public LinqRepositoryBase()
        {
        }

        public abstract DataLoadOptions GetDefaultDataLoadOptions();

        # region IRepository[TEntity]

        /// <summary>
        /// Gets all items list
        /// </summary>
        public virtual List<TEntity> GetAllItems()
        {
            List<TEntity> returnValue = null;
            using (var context = GetNewContext())
            {
                returnValue = GetAllAsQuerable(context).ToList();
                context.SubmitChanges();
                returnValue.ForEach(item => item.Detach());
            }
            return returnValue;
        }

        /// <summary>
        /// Gets item by uniqueidentifier
        /// </summary>
        /// <param name="id">Uniqueidentifier</param>
        public virtual TEntity GetItem(Guid id)
        {
            TEntity returnValue = null;
            using (var context = GetNewContext())
            {
                var mapping = context.Mapping.GetTable(typeof(TEntity));
                var pkField = mapping.RowType.DataMembers.SingleOrDefault(d => d.IsPrimaryKey);

                var itemParameter = Expression.Parameter(typeof(TEntity), "item");
                var whereExpression = Expression.Lambda<Func<TEntity, bool>>(
                    Expression.Equal(
                        Expression.Property(itemParameter, pkField.Name),
                        Expression.Constant(id)),
                        new[] { itemParameter });

                returnValue = GetAllAsQuerable(context).Where(whereExpression).Single();
                returnValue.Detach();
            }
            return returnValue;
        }

        /// <summary>
        /// Not supported
        /// </summary>
        public virtual TEntity GetItem(int id)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Saves item
        /// </summary>
        /// <param name="entity"></param>
        public virtual void SaveItem(TEntity entity)
        {
            using (var context = GetNewContext())
            {
                entity.Attach(context);
                context.SubmitChanges();
                entity.Detach();
            }
        }

        /// <summary>
        /// Not supported
        /// </summary>
        public virtual void DeleteItem(int id)
        {
            throw new NotSupportedException();
        }

        /// <summary>
        /// Deleted item by id
        /// </summary>
        /// <param name="id">Uniqueidentifier</param>
        public virtual void DeleteItem(Guid id)
        {
            using (var context = GetNewContext())
            {
                var entity = GetItem(id);
                entity.Attach(context);
                context.GetTable<TEntity>().DeleteOnSubmit(entity);
                context.SubmitChanges();
            }
        }

        /// <summary>
        /// Geta all items by id dictionary
        /// </summary>
        public Dictionary<Guid, TEntity> GetAllItemsByUniqueIdDictionary()
        {
            return GetAllItems().ToDictionary(item => item.UniqueId.Value);
        }

        /// <summary>
        /// Not supported
        /// </summary>
        public Dictionary<int, TEntity> GetAllItemsByIdDictionary()
        {
            throw new NotSupportedException();
        }

        #  endregion

        /// <summary>
        /// Gets items by expression
        /// </summary>
        /// <param name="expression">Search condition</param>
        public List<TEntity> GetByExpression(Expression<Func<TEntity, bool>> expression)
        {
            if (expression == null)
                throw new ArgumentNullException("expression");

            List<TEntity> returnValue = null;
            using (var context = GetNewContext())
            {
                returnValue = GetAllAsQuerable(context).Where(expression).ToList();
                returnValue.ForEach(item => item.Detach());
            }
            return returnValue;
        }

        /// <summary>
        /// Get all items as querable
        /// </summary>
        protected IQueryable<TEntity> GetAllAsQuerable(TContext context)
        {
            return context.GetTable<TEntity>();
        }

        protected TContext GetNewContext()
        {
            return new TContext()
            {
                LoadOptions = GetDefaultDataLoadOptions()
            };
        }
    }
}
